🌬️CPAPDex
CPAPDex senses one signal — the night of CPAP therapy — by decoding the machine’s own EDF/EDF+ files. It carries the suite’s one genuinely new capability: a pure-JS EDF binary reader. It is a working scaffold, built layer-clean from day one.
Signal sensed
CPAP therapypressure · flow · leak · device events
Device
ResMed AirSenseEDF/EDF+ therapy logs
Input format
EDF / EDF+ binaryBRP / PLD / SA2 / EVE / CSL file sets
Produces
Therapy metrics + events→ ganglior.node-export
01 — WHAT IT'S FOR
A single-signal receptor
Read the suite as a reflex arc: receptors → relay → integration → insight. CPAPDex is an afferent receptor — it does exactly one transduction well and reports inward. It never reaches into another node; cross-signal work happens only through the Ganglior export, consumed by the Integrator.
🧠One signal per node. CPAPDex senses cpap therapy and nothing else. If you’re tempted to sense a second signal, that’s a second node — fusion is the Integrator’s job, never a receptor’s.
02 — THE WIRING
The file stack
CPAPDex is built from external *.js files referenced by CPAPDex.src.html and bundled to a standalone CPAPDex.html. Dependencies point downhill only — UI → DSP → Core — and each file owns exactly one job. Edit the .js + .src.html, never the bundle; re-bundle after changes.
DSP
cpapdex-edf.jsDSP
The suite’s one new capability: a dependency-free EDF/EDF+ binary decoder. Parses headers, decodes int16 samples to physical units, reads annotation TALs (EVE/CSL), and maps EDF start date/time to floating t0Ms.
cpapdex-dsp.jsDSP
Consumes decoded EDF records and produces per-session + per-night therapy metrics: maskOn detection, the 14 manifest metrics, session merge by timestamp prefix, and the night anchor.
Core
kernel-constants.jsCore · shared
The frozen physiology kernel — one source of truth for every cross-fleet threshold, plus a content-derived hash stamped into each export so two bundles built on different rulebooks can’t silently “agree” when fused.
ganglior-provenance.jsCore · shared
Loaded first — installs the read-hook that makes every export attributable to the exact code + inputs that produced it (the buildHash provenance stamp). 100% local.
metric-registry.jsCore · shared
The shared half of the evidence system: the badge SHAPE, depth tiering and the injected badge CSS. The per-node registry below supplies the DATA, so a Validated dot looks identical in every node.
crossnight-envelope.jsCore · shared
The shared, versioned shape (ganglior.crossnight v1.0) every node emits and the Integrator reads — the container, not the math (the local crossNight() stats stay per-node).
cpapdex-registry.jsCore
Per-metric DATA map. Device-scored EVE/CSL event indices are the measured top tier; derived indices grade validated/emerging.
Render · UI
cpapdex-render.jsRender · UI
Cohesion-native HTML-string builders: KPIs, residual/pressure/leak/ventilation/oximetry-QC cards, dependency-free canvas charts (AHI-by-hour, pressure distribution) and the Ganglior event stream.
App · glue
cpapdex-app.jsApp · glue
Loaded last: clusters a dropped AirSense file-set into sessions by filename timestamp, decodes → buildNight, paints the views, wires export. 100% local.
Fusion · export
cpapdex-fusion.jsFusion · export
Builds the Ganglior events (apnea/hypopnea/periodic-breathing/desat/large-leak — device-scored EVE apneas are the top tier), the node-export, cross-metrics (AHI↔ODI, leak↔AHI) and the full metrics table.
cpapdex-coimport.jsFusion · export
Optional ingest of a peer node’s export to upgrade the read: an OxyDex export supplies borrowed oximetry (ODI/T90); an ECGDex export adds autonomic corroboration of device-scored apneas.
cpapdex-cross.jsFusion · export
The byte-identical multi-night analytics helper, weighting nights by therapy-hour completeness.
🕒The Clock Contract. CPAPDex stamps every record as floating wall-clock tMs (local civil time encoded as if UTC) and reads it back with getUTC* — so the recording reads identically in any viewer’s timezone, and two devices recording the same minute land on the same tMs. parseTimestamp is duplicated locally by design.
03 — HOW IT'S ACHIEVED
From file drop to Ganglior event
The pipeline is the same shape across the suite: ingest → parse on the canonical clock → compute in DSP → grade & render in UI → emit onto the bus. No number is computed in the render layer; it asks DSP for the value and the registry for how to show it.
Drop the AirSense file-set
A night is several EDF files (BRP/PLD/SA2/EVE/CSL). They’re clustered into sessions by the filename timestamp prefix (YYYYMMDD_HHMMSS).
cpapdex-app.js → groupEdfFiles()
Decode the EDF binary
The pure-JS reader parses headers, decodes int16 samples to physical units, and reads EVE/CSL annotation TALs — entirely in-browser.
cpapdex-edf.js
Stamp the clock
EDF carries local civil time with no zone → floating t0Ms via Date.UTC, offsetMin = null, read back with getUTC*.
Clock Contract §3
Build the night
Per-session metrics are computed (maskOn, the 14 manifest metrics), then merged and anchored into one night object.
cpapdex-dsp.js → buildNight()
Fuse + co-import + emit
Optionally upgrade with an OxyDex/ECGDex export, then emit device-scored apnea/leak events onto Ganglior.
cpapdex-fusion.js + coimport.js
04 — HOW MUCH TO TRUST IT
The evidence ladder
Every metric CPAPDex surfaces carries an evidence grade — a non-hue badge whose fill is the confidence ladder: solid bullseye for a raw reading, down to a dashed ring for a friendly estimate. A measurement and a projection must never look alike. Grades live in cpapdex-registry.js, the one source of truth.
Measured
Device-scored EVE/CSL event indices — the AirSense’s own apnea/hypopnea scoring
Validated
AHI · residual event index · 95th-percentile pressure · leak rate · compliance hours
Emerging
Periodic-breathing detection · AHI↔ODI concordance (borrowed oximetry)
Experimental
Autonomic corroboration of device apneas (ECGDex co-import)
Heuristic
(none currently surfaced)
05 — LIMITATIONS & CONSIDERATIONS
What it can and can't say
Honesty is architectural: a guess is never dressed as a measurement, a missing value stays null rather than fabricated, and confidence is kept separate from signal quality. The node-specific caveats:
This is a scaffoldcaution
CPAPDex is a faithful starting point, not a finished node. The EDF reader and metrics are real; some longitudinal pieces (e.g. compliance) are defined but still maturing.
SA2 oximeter is often absentnote
The CPAP’s own oximeter channel is frequently not connected. Co-importing an OxyDex night supplies real ODI/T90 — clearly labelled as external.
Device events are top tierby design
The AirSense’s own EVE-scored apneas are the highest evidence tier — CPAPDex trusts the device’s scoring over any re-derivation.
Born layer-cleanlimitation
Unlike the older nodes, CPAPDex was built with edf / dsp / render / fusion separated from day one — the target architecture, adopted forward.
06 — HOW TO USE IT
For the curious, and for the next coder
📂
Open the instrument
CPAPDex is one self-contained file. Open CPAPDex.html in any browser — online, offline, or from file://. Nothing installs, nothing phones home.
Open CPAPDex →
📥
Drop your recording
Drag a ResMed AirSense export (edf / edf+ binary) onto the upload zone. It’s read in-browser and turned into metrics on your device alone.
🎚️
Read at your depth
Start in Core for the headline readiness and a plain-language line. Step up to Advanced for full tables and charts, or Research for composites and the full dump. Each metric’s evidence dot tells you how far to trust it.
🔀
Export onto Ganglior
Export a ganglior.node-export JSON to feed the Integrator, or to upgrade a peer node’s read. Events carry a wall-clock t; consumers reconstruct absolute time from the recording start.
CPAPDex reference guide →
🛠️
If you’re changing it
Edit the cpapdex-*.js files or CPAPDex.src.html — never the bundled CPAPDex.html — then re-bundle. Run Dex-Test-Suite.html until all-green, and verify-provenance.html after re-bundling.